package com.mth480.rebasedownloader;

import java.util.*;
import java.io.*;
import java.net.*;
import java.text.*;
// import com.biomatters.geneious.publicapi.*;
// import com.biomatters.geneious.publicapi.documents.*;
import com.thoughtworks.xstream.*;
// import com.thoughtworks.xstream.persistence.*;
// import java.lang.reflect.*;

public class RebaseDataRetriever {
	private static String urlAddress = "http://rebase.neb.com:80/rebase/link_parsrefs";
	private static String relativeDataFileLocation = "rebaseData/rebaseDataFile.data";
	private static String relativeXmlFileLocation = "rebaseData/";
	static ArrayList<RestrictionEnzymeData>  completeEnzymeList;
	
	/*
	 * Stuff related to getting data from xml files
	 */
	
	/**
	 * If an paremeter is null or empty it will be ignored during filtering.
	 * You should check for updated rebase file before using this!
	 * @param enzymeName
	 * @param prototype
	 * @param microorganism
	 * @param methSiteAndType
	 * @param commercialSource
	 * @return List of RestrictionEnzymes meeting filters passed in
	 */
	public static ArrayList<RestrictionEnzymeData> QueryRebaseFile(String enzymeName, String prototype, String microorganism, String methSiteAndType, String commercialSource, boolean doAnd){	
		if(!LocalRebaseFileExsists() || CheckForUpdatedRebaseDBFile()){
			//updaterGUI updaterGui = new updaterGUI(new java.awt.Frame());
			//updaterGui.setDefaultCloseOperation( javax.swing.JFrame.DO_NOTHING_ON_CLOSE );
			//updaterGui.progressBar.setIndeterminate(true);
			//updaterGui.setVisible(true);
			//updaterGui.AddToLog("Starting Rebase Data Updater...");
			//if(LocalRebaseFileExsists()){
			//	updaterGui.AddToLog("Local Database File Found.");
			//	if(CheckForUpdatedRebaseDBFile()) updaterGui.AddToLog("Found new rebase file on web server.");
			//}
			//else{
			//	updaterGui.AddToLog("Local Database File NOT Found.");
			//}
			
			//updaterGui.AddToLog("Begining web server data file download...");
			//updaterGui.progressBar.setStringPainted(true);
			//updaterGui.progressBar.setIndeterminate(false);
			UpdateRebaseDBFile();//updaterGui);
			//updaterGui.progressBar.setIndeterminate(true);
			//updaterGui.progressBar.setStringPainted(false);
			//updaterGui.AddToLog("Web server data file download compeleted");
			
			//updaterGui.AddToLog("Converting rebase data file to xml...");
			ConvertRebaseDbIntoXmlFiles();//updaterGui);
			//updaterGui.AddToLog("\n\r Convertion process completed.");
			//updaterGui.progressBar.setIndeterminate(false);
			
			//updaterGui.AddToLog("\n\r\n\r Update process completed.");
			//updaterGui.okButton.setEnabled(true);
			//updaterGui.setDefaultCloseOperation( javax.swing.JFrame.DISPOSE_ON_CLOSE );
		}
		
		return queryRebaseFile(enzymeName, prototype, microorganism, methSiteAndType, commercialSource, doAnd );
	}
	
	private static ArrayList<RestrictionEnzymeData> queryRebaseFile(String enzymeName, String prototype, String microorganism, String methSiteAndType, String commercialSource, boolean doAnd)
	{
		// clean strings
		enzymeName = enzymeName.trim().toLowerCase();
		prototype = prototype.trim().toLowerCase();
		microorganism = microorganism.trim().toLowerCase();
		methSiteAndType = methSiteAndType.trim().toLowerCase();
		commercialSource = commercialSource.trim().toLowerCase();
		
		if(completeEnzymeList==null ||completeEnzymeList.size()==0)completeEnzymeList = loadRebaseDataFromXml();
		ArrayList<RestrictionEnzymeData> resultsList = new ArrayList<RestrictionEnzymeData>();
		for (RestrictionEnzymeData reData : completeEnzymeList){
			if(doAnd) {
				// do filter checks here
				if(enzymeName.length()!=0){
					if(reData.enzymeName == null){ continue; }
					else if(reData.enzymeName.toLowerCase().lastIndexOf(enzymeName)==-1){ continue; }
				}
				if(prototype.length()!=0) {
					if(reData.prototype == null){ continue; }
					else if(reData.prototype.toLowerCase().lastIndexOf(prototype)==-1){ continue; }
				}
				if(microorganism.length()!=0) {
					if(reData.microorganism == null){ continue; }
					else if(reData.microorganism.toLowerCase().lastIndexOf(microorganism)==-1){ continue; }
				}
				if(methSiteAndType.length()!=0) {
					if(reData.methylationSiteAndType == null){ continue; }
					else if(reData.methylationSiteAndType.toLowerCase().lastIndexOf(methSiteAndType)==-1){ continue; }
				}
				if(commercialSource.length()!=0) {
					if(reData.commercialSource == null){ continue; }
					else if(reData.commercialSource.toLowerCase().lastIndexOf(commercialSource)==-1){ continue; }
				}
					
				// System.out.println("Query found "+reData.enzymeName);
				resultsList.add(reData);
			}
			else {  // else 'else
				// do filter checks here
				if(enzymeName.length()!=0 && reData.enzymeName != null && reData.enzymeName.toLowerCase().lastIndexOf(enzymeName)!=-1) resultsList.add(reData);
				else if(prototype.length()!=0 && reData.prototype != null && reData.prototype.toLowerCase().lastIndexOf(prototype)!=-1) resultsList.add(reData);
				else if(microorganism.length()!=0 && reData.microorganism != null && reData.microorganism.toLowerCase().lastIndexOf(microorganism)!=-1) resultsList.add(reData);
				else if(methSiteAndType.length()!=0 && reData.methylationSiteAndType != null && reData.methylationSiteAndType.toLowerCase().lastIndexOf(methSiteAndType)!=-1) resultsList.add(reData);
				else if(commercialSource.length()!=0 && reData.commercialSource != null && reData.commercialSource.toLowerCase().lastIndexOf(commercialSource)!=-1) resultsList.add(reData);
			}
		}
		return resultsList;
	}

	private static ArrayList<RestrictionEnzymeData> loadRebaseDataFromXml(){
		XStream xstream = new XStream();	
		ArrayList<RestrictionEnzymeData> list = new ArrayList<RestrictionEnzymeData>();
		
	    File dir = new File(relativeXmlFileLocation);
	    String[] children = dir.list();
	    FilenameFilter filter = new FilenameFilter() {
	        public boolean accept(File dir, String name) {
	            return name.endsWith(".rxml");
	        }
	    };
	    children = dir.list(filter);
	    
	    // TODO: Better exception handler
	    int count = 0;
	    try{
		    for(int i=0; i<children.length;i++){
		    	// System.out.println("\tloading "+children[i]);
		        BufferedReader in = new BufferedReader(new FileReader(relativeXmlFileLocation+children[i]));
		        list.add((RestrictionEnzymeData) xstream.fromXML(in));
		        //list.add((RestrictionEnzymeData) xstream.fromXML(in));
		    	in.close();
		    	count++;
		    }
	    }
	    catch(IOException ioe){
	    	ioe.printStackTrace();
	    }
		
	    // System.out.println("Read in "+count+" files...");
		return list;
	}
	
	/**
	 * Stuff related to parsing giant data file into XML
	 */
	//public static void ConvertRebaseDbIntoXmlFiles(updaterGUI gui){
	public static void ConvertRebaseDbIntoXmlFiles(){
		/* Record Format:
			<1>enzymename
			<2>prototype
			<3>microorganism
			<4>source
			<5>rec seq
			<6>meth site
			<7>com source
			<8>refrences
		 */
		
		try{
			DataInputStream in = new DataInputStream(new FileInputStream(relativeDataFileLocation));
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			
			
			String strLine;
			int state = 1;
			RestrictionEnzymeData currentEnzyme = new RestrictionEnzymeData();
			
			int enzymeNumber = 0;
			
			//Read File Line By Line
			while ((strLine = br.readLine()) != null)   {
				// System.out.print(state);
				strLine = strLine.trim();
				// starting new enzyme
				if (strLine.equals("References:"))break;
				else if(strLine.length()<3) state = 1;	// blank lines mean we should start new enzyme
				else if(state==1){
					currentEnzyme = new RestrictionEnzymeData();
					if(strLine.charAt(1)=='1'){	// check for expected data
						currentEnzyme.enzymeName = strLine.substring(3);
						state++;
					}
				}
				else if(state==2){
					if (strLine.charAt(1)=='2'){	// check for expected data
						if(strLine.length()>=4) currentEnzyme.prototype = strLine.substring(3);
						state++;
					}
					// TODO: Add error msg on unexpected data
					else state = 1;	// reset when unexpected data found
				}
				else if(state==3){
					if(strLine.charAt(1)=='3'){	// check for expected data
						if(strLine.length()>=4) currentEnzyme.microorganism = strLine.substring(3);
						state++;
					}
					else state = 1;	// reset when unexpected data found
				}
				else if(state==4){
					if(strLine.charAt(1)=='4'){	// check for expected data
						if(strLine.length()>=4) currentEnzyme.source = strLine.substring(3);
						state++;
					}
					else state = 1;	// reset when unexpected data found
				}
				else if(state==5){
					if(strLine.charAt(1)=='5'){	// check for expected data
						currentEnzyme.recognitionSequence = strLine.substring(3);
						state++;
					}
					else state = 1;	// reset when unexpected data found
				}
				else if(state==6){
					if(strLine.charAt(1)=='6'){	// check for expected data
						if(strLine.length()>=4) currentEnzyme.methylationSiteAndType = strLine.substring(3);
						state++;
					}
					else state = 1;	// reset when unexpected data found
				}
				else if(state==7){
					if(strLine.charAt(1)=='7'){	// check for expected data
						if(strLine.length()>=4) currentEnzyme.commercialSource = strLine.substring(3);
						state++;
					}
					else state = 1;	// reset when unexpected data found
				}
				else if(state==8){
					if(strLine.charAt(1)=='8'){	// check for expected data
						// We dont care about the refenrences data
						currentEnzyme.FileNumber = enzymeNumber;
						CreateResourceFile(currentEnzyme, enzymeNumber);
						enzymeNumber++;
						//if(enzymeNumber%10==0) gui.setLastLineOfLog("Converted "+enzymeNumber+" restriction enzymes");
						state = 1;
					}
					else state = 1;	// reset when unexpected data found
				}
				else state=1;	// somethings wrong if you reach here
			}
			
			in.close();
		}catch (Exception e3){
			e3.printStackTrace();
		}
	}
	
	private static void CreateResourceFile(RestrictionEnzymeData REdata, int FileNumber) throws IOException
	{
		XStream xstream = new XStream();	
		// System.out.println("\tcreating xml file "+FileNumber);
	   try {
	        BufferedWriter out = new BufferedWriter(new FileWriter(relativeXmlFileLocation+"/"+FileNumber+".rxml"));
	        out.write(xstream.toXML(REdata));
	        out.close();
	    } catch (IOException e) {
	    }
	}
	
	
	/**
	 * stuff related to getting data file from rebase
	 */
	
	public static boolean LocalRebaseFileExsists()
	{
		File file = new File(relativeDataFileLocation);
		if(file.length()==0F)return false;
		
		return file.exists();
	}
	
	/**
	 * Checks rebase site to see if there is a newer rebase data file than on local machine
	 * @return true if DB version on local machine is older than one on Rebase site
	 */
	public static boolean CheckForUpdatedRebaseDBFile(){
	    // get last update of Offical Rebase File
		Date officalDate = new Date();
		try {
	        URL url = new URL(urlAddress);
	        URLConnection conn = url.openConnection();
	        conn.setRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; H010818)" );
	        DateFormat df = new SimpleDateFormat("dd MMM yyyy");
	        officalDate = (Date)df.parse(conn.getHeaderField("Last-modified").substring(5, 16));
	        // System.out.println("\nupdated: "+officalDate.toString());
	    } catch (Exception e) {
	    	// System.out.println("\nError getting last mod: "+e.getMessage());
	    	// TODO: what to do on error?
	    }
	    
	    // get last update of local rebase file
	    File file = new File(relativeDataFileLocation);
	    if(file.lastModified()==0L)return true;	// no file on local
	    Date modifiedTime = new Date(file.lastModified());
	    
	    // compare results to determine if there is a newer version
	    return officalDate.compareTo(modifiedTime) > 0;
	}
	
	/**
	 * @return true on succesful update otherwise false
	 */
	//public static boolean UpdateRebaseDBFile(updaterGUI gui){
	public static boolean UpdateRebaseDBFile(){
		OutputStream out = null;
		URLConnection conn = null;
		InputStream  in = null;
		try 
		{
			URL url = new URL(urlAddress);
			
			File folder = new File(relativeXmlFileLocation);
			if(!folder.exists()) folder.mkdir();
			
			out = new BufferedOutputStream(
				new FileOutputStream(relativeDataFileLocation));	
			conn = url.openConnection();
			conn.setRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; H010818)" );
			int totalSize = Integer.parseInt(conn.getHeaderField("content-length"));
			//gui.progressBar.setMaximum(totalSize);
			in = conn.getInputStream();
			byte[] buffer = new byte[1024];
			int numRead;
			long numWritten = 0;
			// System.out.println("Getting updated file...");
			while ((numRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, numRead);
				numWritten += numRead;
				//gui.progressBar.setValue((int)numWritten);
				// TODO: Pass thread object so we can update GUI here
				//// System.out.println("\n\tWritten so far: " + numWritten);
			}
			// System.out.println("\n Done! "+relativeDataFileLocation + "\t~ " + numWritten);
		} 
		catch (Exception exception) 
		{
			exception.printStackTrace();
			return false;
		} 
		finally 
		{
			try 
			{
				if (in != null) {
					in.close();
				}
				if (out != null) {
					out.close();
				}
			} 
			catch (IOException ioe) {}
		}
		return true;
	}
	
	/**
	 * Tester
	 */
	
	public static void main(String[] args){		
		QueryRebaseFile("Aa", "", "", "", "", true);
		 System.out.println("2nd search");
		QueryRebaseFile("AagI", "", "", "", "", true);
		 System.out.println("done");
	}
}